package com.dl.config.shiro;

import com.dl.config.serializable.MyByteSource;
import com.dl.pojo.Permission;
import com.dl.pojo.Role;
import com.dl.pojo.User;
import com.dl.service.UserService;
import com.dl.utils.ConstantField;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;

/**
 * 自定义的UserRealm，可以有多个realm
 */
@Component
public class UserRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("======================授权处理开始=====================");
        /**
         * principalCollection.getPrimaryPrincipal();实际上拿到的是
         * new SimpleAuthenticationInfo(user,user.getPassword(),salt,this.getName());中的第一个参数
         * */
        String username = (String) principalCollection.getPrimaryPrincipal();

        if (username != null && !username.equals("")){
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //获取该用户角色集合
            List<Role> roles = userService.findRolesByUsername(username);
            //获取该用户权限集合
            List<Permission> permissions = userService.findPermissionsByUsername(username);

            //如果是管理员则添加所有权限
            if ("admin".equals(username)){
                info.addStringPermission("*:*");
            }

            //添加角色
            roles.forEach(role -> { info.addRole(role.getName()); });
            //添加权限
            permissions.forEach(permission -> { info.addStringPermission(permission.getName()); });

            Session session = SecurityUtils.getSubject().getSession();
            User user = (User) session.getAttribute(ConstantField.LOGIN_INFO);
            user.setRoles(roles);
            user.setPermissions(permissions);
            session.setAttribute(ConstantField.LOGIN_INFO, user);
            System.out.println("======================授权处理结束=====================");
            return info;
        }
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("======================认证处理开始=====================");

        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        //拿到user对象
        User user = userService.queryUserByName(userToken.getUsername());

        if (user != null) {//查询到用户
            if (user.getStatus() == 0){
                throw new LockedAccountException("账号已被锁定，请联系管理员");
            }else{
                //ByteSource salt =  ByteSource.Util.bytes(user.getSalt());
                MyByteSource salt = new MyByteSource(user.getSalt());//自定义的盐处理实现序列化接口

                //获取用户主体
                Subject subject = SecurityUtils.getSubject();
                //获取shiroSession
                Session shiroSession = subject.getSession();
                //用户信息放入session
                shiroSession.setAttribute(ConstantField.LOGIN_INFO, user);
                System.out.println("======================认证处理结束=====================");
                //密码验证shiro来做，进行了加密。参数一：用户名或用户，参数二：加密后的密码，参数三：随机盐，参数四：realm的名字
                return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), salt, this.getName());
            }
        }
        //未查询到用户
        return null; //UnknownAccountException
    }

    /**
     * 清除指定用户的授权缓存
     * @param username
     */
    public void clearAuthorizationInfoCache(String username) {
        System.out.println("清除--" + username + "--用户的授权缓存");
        Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
        System.out.println("cache = " + cache);
        cache.remove(username);
    }

    /**
     * 退出时删除用户缓存
     * @param principals
     */
    @Override
    protected void doClearCache(PrincipalCollection principals) {
        super.doClearCache(principals);
    }
}
